#include <gtk/gtk.h>
#include <stdlib.h>
#include <main.h>
#include <engine_if.h>
#include <librsvg/rsvg.h>
#include <librsvg/rsvg-cairo.h>

#define WINDOW_TITLE "udock"
#define WINDOW_HEIGHT 80

DockInterface* udock_itemengine_interface = NULL;
GList* itemEngines = NULL;
GList* items = NULL;
GHashTable* itemLayoutData = NULL;
GHashTable* engineEventCallbacks = NULL;

GKeyFile* keyfile;

gboolean alpha_support = TRUE;

/* options */
guint item_default_width = 48;
guint item_default_height = 48;
guint item_gap = 4;
guint udock_border_left = 10;
guint udock_border_right = 10;
guint udock_border_line_width = 1;

void udock_window_create();
void udock_shutdown();
gboolean udock_window_init(GtkWidget* win, GdkEventExpose* event, gpointer userdata);
void udock_window_set_colormap(GtkWidget* win, GdkScreen* old_screen, gpointer userdata);
void udock_settings_load();
guint udock_window_calculate_width();
guint udock_window_calculate_height();
void udock_settings_create_initial_config_file();
void udock_itemengines_load(gchar**,guint);
void udock_item_add(DockItem*,DockItemEngine*);
void udock_item_request_redraw(DockItem*);
DockItemLayoutData* udock_item_get_layout_data(DockItem*);

gchar* udock_keyfile_get_string(gchar* section, gchar* key);
guint udock_keyfile_get_int(gchar* section, gchar* key);
gboolean udock_keyfile_get_boolean(gchar* section, gchar* key);
gchar** udock_keyfile_get_string_list(gchar* section, gchar* key, guint* length);
gint* udock_keyfile_get_int_list(gchar* section, gchar* key, guint* length);

void udock_window_redraw(cairo_t*);

static gboolean udock_window_mouse_enter_notify(GtkWidget* widget,
						GdkEventCrossing* event,
						gpointer data);
static gboolean udock_window_mouse_leave_notify(GtkWidget* widget,
						GdkEventCrossing* event,
						gpointer data);
static gboolean udock_window_mouse_motion_notify(GtkWidget* widget, 
						 GdkEventMotion* event,
						 gpointer data);


int main(int argc, char** argv) {
  
  gtk_init(&argc, &argv);

  udock_settings_load();
  udock_window_create();

  gtk_main();

  return 0;
}

void udock_window_create() {
  GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), WINDOW_TITLE);

  gtk_widget_add_events(window,
			GDK_POINTER_MOTION_MASK |
			GDK_POINTER_MOTION_HINT_MASK);

  g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(udock_shutdown), NULL);
  g_signal_connect(G_OBJECT(window), "expose-event", G_CALLBACK(udock_window_init), NULL);
  g_signal_connect(G_OBJECT(window), "screen-changed", G_CALLBACK(udock_window_set_colormap), NULL);
  g_signal_connect(G_OBJECT(window), "motion-notify-event", G_CALLBACK(udock_window_mouse_motion_notify), NULL);
  g_signal_connect(G_OBJECT(window), "enter-notify-event", G_CALLBACK(udock_window_mouse_enter_notify), NULL);
  g_signal_connect(G_OBJECT(window), "leave-notify-event", G_CALLBACK(udock_window_mouse_leave_notify), NULL);

  gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
  gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);
  
  udock_window_set_colormap(window, NULL, NULL);

  gtk_widget_show_all(window);

  guint window_new_width = udock_window_calculate_width();
  guint window_new_height = udock_window_calculate_height();
  gtk_window_resize(GTK_WINDOW(window), window_new_width, window_new_height);

}

/* enables alpha channel support */
void udock_window_set_colormap(GtkWidget* win, GdkScreen* old_screen, gpointer userdata) {
  GdkScreen* screen = gtk_widget_get_screen(win);
  GdkColormap* colormap = gdk_screen_get_rgba_colormap(screen);

  if (!colormap) {
    g_print("You need a composition manager to run udock with transparency support.\n");
    colormap = gdk_screen_get_rgb_colormap(screen);
    alpha_support = FALSE;
  }

  gtk_widget_set_colormap(win, colormap);
}

/* resizes and moves the window */
gboolean udock_window_init(GtkWidget* win, GdkEventExpose* event, gpointer userdata) {
  g_print("expose()\n");

  GdkScreen* screen = gtk_window_get_screen(GTK_WINDOW(win));

  guint newX = gdk_screen_get_width(screen) / 2 - udock_window_calculate_width() / 2;
  guint newY = gdk_screen_get_height(screen) - udock_window_calculate_height();

  gtk_window_move(GTK_WINDOW(win), newX, newY);

  cairo_t* cr = gdk_cairo_create(win->window);
  udock_window_redraw(cr);
  /*  cairo_destroy(cr); */

  return TRUE;
}

void udock_window_redraw(cairo_t* cr) {
  /* This is where the magic happens ;-) */

  g_print("udock_window_redraw()\n");

  gdouble xpos = udock_border_line_width + udock_border_left;
  
  GList* iterator;
  for (iterator = items; iterator != NULL; iterator = iterator->next) {
    DockItem* item = iterator->data;
    DockItemLayoutData* data = udock_item_get_layout_data(item);
    data->x = xpos;

    cairo_save(cr);
    if (data->magnificationFactor > 1.00) {
      cairo_scale(cr, data->magnificationFactor, data->magnificationFactor);
    }

    guint ypos = udock_window_calculate_height() - item_default_height;
    data->y = ypos;

    DockItemEngine* engine = data->engine;
    cairo_surface_t* imageData = engine->render(item, cr);
    if (imageData == NULL) {
      g_print("imageData is null\n");
    }
    data->renderCache = imageData;
    cairo_set_source_surface(cr, imageData, xpos, ypos);
    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
    cairo_paint(cr);

    cairo_restore(cr);

    xpos += data->width;
    if (iterator->next != NULL) {
      xpos += item_gap;
    }
  }

  g_print("%i\n", udock_window_calculate_width());


}

/* loads the settings from ~/.udock/config */
void udock_settings_load() {
  gchar* config_filename = g_strconcat(getenv("HOME"), "/.udock/config", NULL);

  g_print("%s\n", config_filename);
  
  keyfile = g_key_file_new();
  GError* error = NULL;
  g_key_file_load_from_file(keyfile, config_filename, G_KEY_FILE_NONE, &error);

  if (error != NULL) {
    if (error->code == G_FILE_ERROR_NOENT || error->code == G_KEY_FILE_ERROR_NOT_FOUND)
      udock_settings_create_initial_config_file();
    else {
      if (error->code == G_KEY_FILE_ERROR_PARSE) {
	g_print("Found errors in config file. Using defaults.\n");
      } else {
	g_print("Problem opening the config file. Using defaults\n");
      }
    }
  }

  /* FIXME: load settings
     item_gap = ... 
     item_default_width = ...
     item_default_height = ... */

  guint length;
  error = NULL;
  gchar** engine_list = g_key_file_get_string_list(keyfile, "General Settings", "ItemEngines", &length, &error);
  if (engine_list == NULL) {
    if (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
      g_print("No modules available\n");
      /* gtk_exit(); */
    }
    if (error->code == G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
      g_print("Problem with the key file: General Settings Section not found.\n");
      /* gtk_exit(); */
    }
  }

  udock_itemengines_load(engine_list, length);
}

/* calculates the window's width */
guint udock_window_calculate_width() {
  guint dock_width = udock_border_line_width;
  dock_width = dock_width + udock_border_left;
  GList* iterator;
  for (iterator = items; iterator != NULL; iterator = iterator->next) {
    DockItemLayoutData* data = udock_item_get_layout_data(iterator->data);
    guint item_current_width = data->width * data->magnificationFactor;
    dock_width = dock_width + item_current_width;
    if (iterator->next != NULL) {
      dock_width = dock_width + item_gap;
    }
  }
  dock_width = dock_width + udock_border_right;
  dock_width = dock_width + udock_border_line_width;
  return dock_width;
}

/* calculates the window's height */
guint udock_window_calculate_height() {
  /* FIXME: calculate instead of using constant values */
  return WINDOW_HEIGHT;
}

void udock_itemengines_load(gchar** engine_names, guint engine_count) {
  /* initialize interface for DockItemEngines to interact with the dock */
  udock_itemengine_interface = g_new(DockInterface, 1);
  udock_itemengine_interface->dock_item_register_callback = udock_item_add;
  udock_itemengine_interface->dock_item_request_redraw_callback = udock_item_request_redraw;
  udock_itemengine_interface->default_item_width = item_default_width;
  udock_itemengine_interface->default_item_height = item_default_height;
  udock_itemengine_interface->dock_keyfile_get_string = udock_keyfile_get_string;
  udock_itemengine_interface->dock_keyfile_get_int = udock_keyfile_get_int;
  udock_itemengine_interface->dock_keyfile_get_boolean = udock_keyfile_get_boolean;
  udock_itemengine_interface->dock_keyfile_get_string_list = udock_keyfile_get_string_list;
  udock_itemengine_interface->dock_keyfile_get_int_list = udock_keyfile_get_int_list;

  itemLayoutData = g_hash_table_new(g_direct_hash, g_direct_equal);
  engineEventCallbacks = g_hash_table_new(g_direct_hash, g_direct_equal);  

  guint i;
  for (i = 0; i < engine_count; i++) {
    DockItemEngine* engine = g_new(DockItemEngine, 1);
    DockItemEngineEventCallbacks* callbacks = g_new(DockItemEngineEventCallbacks, 1);

    /* FIXME: write .so-lookup routine to look for .so files in ~/.udock/modules and /usr/share/udock/modules */

    gchar* absfilename = g_strconcat(getenv("HOME"), "/.udock/modules/", engine_names[i], NULL);

    engine_load(absfilename, engine, callbacks);
    g_hash_table_insert(engineEventCallbacks, engine, callbacks);

    g_print("rufe callback auf\n");
    callbacks->engine_init_callback(udock_itemengine_interface, engine);
  }
}

void udock_shutdown() {
  g_free(udock_itemengine_interface);
}

void udock_settings_create_initial_config_file() {
  /* TODO: implement */
}

void udock_item_add(DockItem* item, DockItemEngine* engine) {
  g_print("udock_item_add\n");

  items = g_list_append(items, item);

  DockItemLayoutData* layoutData = g_new(DockItemLayoutData, 1);
  layoutData->x = 0;
  layoutData->y = 0;
  layoutData->width = item_default_width;
  layoutData->height = item_default_height;
  layoutData->magnificationFactor = 1.0;
  layoutData->renderCache = NULL;
  layoutData->labelBuffer = NULL;
  layoutData->labelXOffset = 0;
  layoutData->labelYOffset = 0;
  layoutData->engine = engine;
  layoutData->needsRedraw = TRUE;
  
  g_hash_table_insert(itemLayoutData, item, layoutData);
}

void udock_item_request_redraw(DockItem* item) {
  
}

DockItemLayoutData* udock_item_get_layout_data(DockItem* item) {
  return (DockItemLayoutData*) g_hash_table_lookup(itemLayoutData, item);
}

gchar* udock_keyfile_get_string(gchar* section, gchar* key) {
  /* FIXME: support error handling */
  return g_key_file_get_string(keyfile, section, key, NULL);
}
guint udock_keyfile_get_int(gchar* section, gchar* key) {
  /* FIXME: support error handling */
  return g_key_file_get_integer(keyfile, section, key, NULL);
}
gboolean udock_keyfile_get_boolean(gchar* section, gchar* key) {
  /* FIXME: support error handling */
  return g_key_file_get_boolean(keyfile, section, key, NULL);
}
gchar** udock_keyfile_get_string_list(gchar* section, gchar* key, guint* length) {
  /* FIXME: support error handling */
  return g_key_file_get_string_list(keyfile, section, key, length, NULL);
}
gint* udock_keyfile_get_int_list(gchar* section, gchar* key, guint* length) {
  /* FIXME: support error handling */
  return g_key_file_get_integer_list(keyfile, section, key, length, NULL);
}

static gboolean udock_window_mouse_enter_notify(GtkWidget* widget,
						GdkEventCrossing* event,
						gpointer data) {
  g_print("mouse_enter_notify\n");
  return FALSE;
}
static gboolean udock_window_mouse_leave_notify(GtkWidget* widget,
						GdkEventCrossing* event,
						gpointer data) {
  g_print("mouse_leave_notify\n");
  return FALSE;
}
static gboolean udock_window_mouse_motion_notify(GtkWidget* widget, 
						 GdkEventMotion* event,
						 gpointer data) {
  gint mouseX, mouseY;
  
  gdk_window_get_pointer(widget->window, &mouseX, &mouseY, NULL);

  GList* iterator;
  for (iterator = items; iterator != NULL; iterator = iterator->next) {
    DockItem* item = (DockItem*) iterator->data;
    DockItemLayoutData* data = udock_item_get_layout_data(item);

    if (mouseX >= data->x && mouseX <= (data->x + data->width)) {
      DockItemEngine* engine = data->engine;
      DockItemEngineEventCallbacks* callbacks = g_hash_table_lookup(engineEventCallbacks, engine);
      callbacks->mouse_move_callback(item, mouseX, mouseY); /* FIXME: provide relative mouse positions, not absolute */
    }

  }

  return FALSE;
}
